/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package cz.muni.fi.pa165.machineryrental.dao.impl;

import cz.muni.fi.pa165.machineryrental.dao.RentalDAO;
import cz.muni.fi.pa165.machineryrental.entities.Customer;
import cz.muni.fi.pa165.machineryrental.entities.Machine;
import cz.muni.fi.pa165.machineryrental.entities.Rental;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.TypedQuery;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Repository;

/**
 * Implementation of RentalDAO which is used on persisting and other operations
 * on rentals in database.
 *
 * @author Marian Lorenc, 374122
 */
@Repository
@Component
public class RentalDAOImpl implements RentalDAO {

    /**
     * Entity Manager for operations.
     */
	@PersistenceContext(unitName = "ClassicPU")
    private EntityManager em;

    /**
     * Creates new instance of RentalDAOImpl on performing operations on
     * entities into or from database.
     *
     * @param em Entity manager running the operations to database.
     */
    public void setEntityManager(EntityManager em) {
        if (em == null) {
            throw new DataAccessException("Given Entity manager cannot be null.") {};
        }

        this.em = em;
    }

    /**
     * @see cz.muni.fi.pa165.machineryrental.dao.RentalDAO#getById(long)
     */
    @Override
    public Rental getById(long id) {
        if (id < 0) {
            throw new DataAccessException("ID cannot be negative") {};
        }

        return em.find(Rental.class, id);
    }

    /**
     * @see
     * cz.muni.fi.pa165.machineryrental.dao.RentalDAO#create(cz.muni.fi.pa165.machineryrental.entities.Rental)
     */
    @Override
    public void create(Rental rental) {
        if (rental == null) {
            throw new DataAccessException("Rental cannot be null") {};
        }

        this.em.persist(rental);
    }

    /**
     * @see
     * cz.muni.fi.pa165.machineryrental.dao.RentalDAO#update(cz.muni.fi.pa165.machineryrental.entities.Rental)
     */
    @Override
    public void update(Rental rental) {
        if (rental == null) {
            throw new DataAccessException("Rental cannot be null") {};
        }

        this.em.merge(rental);
    }

    /**
     * @see
     * cz.muni.fi.pa165.machineryrental.dao.RentalDAO#remove(cz.muni.fi.pa165.machineryrental.entities.Rental)
     */
    @Override
    public void remove(Rental rental) {
        if (rental == null) {
            throw new DataAccessException("Rental cannot be null") {
            };
        }

        Rental setToFind = em.find(Rental.class, rental.getId());
        if (setToFind == null) {
            throw new PersistenceException("Rental is not in database");
        }
        try {
            em.remove(setToFind);
            em.flush();
        } catch (IllegalArgumentException ex) {
            throw new PersistenceException(ex);
        }
    }

    /**
     * @see cz.muni.fi.pa165.machineryrental.dao.RentalDAO#findAll()
     */
    @Override
    public List<Rental> findAll() {
        TypedQuery<Rental> query = this.em.createQuery("FROM Rental R", Rental.class);
        return query.getResultList();
    }

    /**
     * @see
     * cz.muni.fi.pa165.machineryrental.dao.RentalDAO#findByCustomer(cz.muni.fi.pa165.machineryrental.entities.Customer)
     */
    @Override
    public List<Rental> findByCustomer(Customer customer) {
        if (customer == null) {
            throw new DataAccessException("Customer cannot be null") {};
        }

        TypedQuery<Rental> query = this.em.createQuery("SELECT r FROM Rental r WHERE r.customer = :customer",
                Rental.class);
        query = query.setParameter("customer", customer);
        return query.getResultList();
    }

    /**
     * @see
     * cz.muni.fi.pa165.machineryrental.dao.RentalDAO#findByMachine(cz.muni.fi.pa165.machineryrental.entities.Machine)
     */
    @Override
    public List<Rental> findByMachine(Machine machine) {

        if (machine == null) {
            throw new DataAccessException("Machine cannot be null") {};
        }

        TypedQuery<Rental> query = this.em.createQuery("SELECT r FROM Rental r WHERE r.machine = :machine",
                Rental.class).setParameter("machine", machine);

        return query.getResultList();
    }

	/**
	 * Returns all rentals of given customer and of given
	 * machine
	 * 
	 * @param customer Customer, for which we want to get rentals.
	 * @param machine Machine, for which we want to get rentals.
	 * @return Rentals of given customer and machine.
	 * @throws IllegalArgumentException Throws if given machine is null.
	 * @throws IllegalArgumentException Throws if given customer is null.
	 * @throws IllegalStateException Throws if manager is not initialized.
	 */
	@Override
	public List<Rental> findByCustomerMachine(Customer customer, Machine machine) {
		
		if (machine == null) {
			throw new DataAccessException("Machine cannot be null") {};
		}
		
		if (customer == null) {
			throw new DataAccessException("Customer cannot be null") {};
		}
		
		TypedQuery<Rental> query = this.em.createQuery("SELECT r FROM Rental r WHERE r.machine = :machine AND r.customer = :customer",
                Rental.class).setParameter("machine", machine).setParameter("customer", customer);
		
		return query.getResultList();
	}

}
